home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 4 / Amiga Tools 4.iso / grafix / tools / jpeg / jpeg-6a / rdjpgcom.c < prev    next >
C/C++ Source or Header  |  1995-11-15  |  13KB  |  477 lines

  1. /*
  2.  * rdjpgcom.c
  3.  *
  4.  * Copyright (C) 1994-1995, Thomas G. Lane.
  5.  * This file is part of the Independent JPEG Group's software.
  6.  * For conditions of distribution and use, see the accompanying README file.
  7.  *
  8.  * This file contains a very simple stand-alone application that displays
  9.  * the text in COM (comment) markers in a JFIF file.
  10.  * This may be useful as an example of the minimum logic needed to parse
  11.  * JPEG markers.
  12.  */
  13.  
  14. #define JPEG_CJPEG_DJPEG    /* to get the command-line config symbols */
  15. #include "jinclude.h"        /* get auto-config symbols, <stdio.h> */
  16.  
  17. #include <ctype.h>        /* to declare isupper(), tolower() */
  18. #ifdef USE_SETMODE
  19. #include <fcntl.h>        /* to declare setmode()'s parameter macros */
  20. /* If you have setmode() but not <io.h>, just delete this line: */
  21. #include <io.h>            /* to declare setmode() */
  22. #endif
  23.  
  24. #ifdef USE_CCOMMAND        /* command-line reader for Macintosh */
  25. #ifdef __MWERKS__
  26. #include <SIOUX.h>              /* Metrowerks needs this */
  27. #include <console.h>        /* ... and this */
  28. #endif
  29. #ifdef THINK_C
  30. #include <console.h>        /* Think declares it here */
  31. #endif
  32. #endif
  33.  
  34. #ifdef DONT_USE_B_MODE        /* define mode parameters for fopen() */
  35. #define READ_BINARY    "r"
  36. #else
  37. #define READ_BINARY    "rb"
  38. #endif
  39.  
  40. #ifndef EXIT_FAILURE        /* define exit() codes if not provided */
  41. #define EXIT_FAILURE  1
  42. #endif
  43. #ifndef EXIT_SUCCESS
  44. #ifdef VMS
  45. #define EXIT_SUCCESS  1        /* VMS is very nonstandard */
  46. #else
  47. #define EXIT_SUCCESS  0
  48. #endif
  49. #endif
  50.  
  51.  
  52. /*
  53.  * These macros are used to read the input file.
  54.  * To reuse this code in another application, you might need to change these.
  55.  */
  56.  
  57. static FILE * infile;        /* input JPEG file */
  58.  
  59. /* Return next input byte, or EOF if no more */
  60. #define NEXTBYTE()  getc(infile)
  61.  
  62.  
  63. /* Error exit handler */
  64. #define ERREXIT(msg)  (fprintf(stderr, "%s\n", msg), exit(EXIT_FAILURE))
  65.  
  66.  
  67. /* Read one byte, testing for EOF */
  68. static int
  69. read_1_byte (void)
  70. {
  71.   int c;
  72.  
  73.   c = NEXTBYTE();
  74.   if (c == EOF)
  75.     ERREXIT("Premature EOF in JPEG file");
  76.   return c;
  77. }
  78.  
  79. /* Read 2 bytes, convert to unsigned int */
  80. /* All 2-byte quantities in JPEG markers are MSB first */
  81. static unsigned int
  82. read_2_bytes (void)
  83. {
  84.   int c1, c2;
  85.  
  86.   c1 = NEXTBYTE();
  87.   if (c1 == EOF)
  88.     ERREXIT("Premature EOF in JPEG file");
  89.   c2 = NEXTBYTE();
  90.   if (c2 == EOF)
  91.     ERREXIT("Premature EOF in JPEG file");
  92.   return (((unsigned int) c1) << 8) + ((unsigned int) c2);
  93. }
  94.  
  95.  
  96. /*
  97.  * JPEG markers consist of one or more 0xFF bytes, followed by a marker
  98.  * code byte (which is not an FF).  Here are the marker codes of interest
  99.  * in this program.  (See jdmarker.c for a more complete list.)
  100.  */
  101.  
  102. #define M_SOF0  0xC0        /* Start Of Frame N */
  103. #define M_SOF1  0xC1        /* N indicates which compression process */
  104. #define M_SOF2  0xC2        /* Only SOF0-SOF2 are now in common use */
  105. #define M_SOF3  0xC3
  106. #define M_SOF5  0xC5        /* NB: codes C4 and CC are NOT SOF markers */
  107. #define M_SOF6  0xC6
  108. #define M_SOF7  0xC7
  109. #define M_SOF9  0xC9
  110. #define M_SOF10 0xCA
  111. #define M_SOF11 0xCB
  112. #define M_SOF13 0xCD
  113. #define M_SOF14 0xCE
  114. #define M_SOF15 0xCF
  115. #define M_SOI   0xD8        /* Start Of Image (beginning of datastream) */
  116. #define M_EOI   0xD9        /* End Of Image (end of datastream) */
  117. #define M_SOS   0xDA        /* Start Of Scan (begins compressed data) */
  118. #define M_COM   0xFE        /* COMment */
  119.  
  120.  
  121. /*
  122.  * Find the next JPEG marker and return its marker code.
  123.  * We expect at least one FF byte, possibly more if the compressor used FFs
  124.  * to pad the file.
  125.  * There could also be non-FF garbage between markers.  The treatment of such
  126.  * garbage is unspecified; we choose to skip over it but emit a warning msg.
  127.  * NB: this routine must not be used after seeing SOS marker, since it will
  128.  * not deal correctly with FF/00 sequences in the compressed image data...
  129.  */
  130.  
  131. static int
  132. next_marker (void)
  133. {
  134.   int c;
  135.   int discarded_bytes = 0;
  136.  
  137.   /* Find 0xFF byte; count and skip any non-FFs. */
  138.   c = read_1_byte();
  139.   while (c != 0xFF) {
  140.     discarded_bytes++;
  141.     c = read_1_byte();
  142.   }
  143.   /* Get marker code byte, swallowing any duplicate FF bytes.  Extra FFs
  144.    * are legal as pad bytes, so don't count them in discarded_bytes.
  145.    */
  146.   do {
  147.     c = read_1_byte();
  148.   } while (c == 0xFF);
  149.  
  150.   if (discarded_bytes != 0) {
  151.     fprintf(stderr, "Warning: garbage data found in JPEG file\n");
  152.   }
  153.  
  154.   return c;
  155. }
  156.  
  157.  
  158. /*
  159.  * Read the initial marker, which should be SOI.
  160.  * For a JFIF file, the first two bytes of the file should be literally
  161.  * 0xFF M_SOI.  To be more general, we could use next_marker, but if the
  162.  * input file weren't actually JPEG at all, next_marker might read the whole
  163.  * file and then return a misleading error message...
  164.  */
  165.  
  166. static int
  167. first_marker (void)
  168. {
  169.   int c1, c2;
  170.  
  171.   c1 = NEXTBYTE();
  172.   c2 = NEXTBYTE();
  173.   if (c1 != 0xFF || c2 != M_SOI)
  174.     ERREXIT("Not a JPEG file");
  175.   return c2;
  176. }
  177.  
  178.  
  179. /*
  180.  * Most types of marker are followed by a variable-length parameter segment.
  181.  * This routine skips over the parameters for any marker we don't otherwise
  182.  * want to process.
  183.  * Note that we MUST skip the parameter segment explicitly in order not to
  184.  * be fooled by 0xFF bytes that might appear within the parameter segment;
  185.  * such bytes do NOT introduce new markers.
  186.  */
  187.  
  188. static void
  189. skip_variable (void)
  190. /* Skip over an unknown or uninteresting variable-length marker */
  191. {
  192.   unsigned int length;
  193.  
  194.   /* Get the marker parameter length count */
  195.   length = read_2_bytes();
  196.   /* Length includes itself, so must be at least 2 */
  197.   if (length < 2)
  198.     ERREXIT("Erroneous JPEG marker length");
  199.   length -= 2;
  200.   /* Skip over the remaining bytes */
  201.   while (length > 0) {
  202.     (void) read_1_byte();
  203.     length--;
  204.   }
  205. }
  206.  
  207.  
  208. /*
  209.  * Process a COM marker.
  210.  * We want to print out the marker contents as legible text;
  211.  * we must guard against random junk and varying newline representations.
  212.  */
  213.  
  214. static void
  215. process_COM (void)
  216. {
  217.   unsigned int length;
  218.   int ch;
  219.   int lastch = 0;
  220.  
  221.   /* Get the marker parameter length count */
  222.   length = read_2_bytes();
  223.   /* Length includes itself, so must be at least 2 */
  224.   if (length < 2)
  225.     ERREXIT("Erroneous JPEG marker length");
  226.   length -= 2;
  227.  
  228.   while (length > 0) {
  229.     ch = read_1_byte();
  230.     /* Emit the character in a readable form.
  231.      * Nonprintables are converted to \nnn form,
  232.      * while \ is converted to \\.
  233.      * Newlines in CR, CR/LF, or LF form will be printed as one newline.
  234.      */
  235.     if (ch == '\r') {
  236.       printf("\n");
  237.     } else if (ch == '\n') {
  238.       if (lastch != '\r')
  239.     printf("\n");
  240.     } else if (ch == '\\') {
  241.       printf("\\\\");
  242.     } else if (isprint(ch)) {
  243.       putc(ch, stdout);
  244.     } else {
  245.       printf("\\%03o", ch);
  246.     }
  247.     lastch = ch;
  248.     length--;
  249.   }
  250.   printf("\n");
  251. }
  252.  
  253.  
  254. /*
  255.  * Process a SOFn marker.
  256.  * This code is only needed if you want to know the image dimensions...
  257.  */
  258.  
  259. static void
  260. process_SOFn (int marker)
  261. {
  262.   unsigned int length;
  263.   unsigned int image_height, image_width;
  264.   int data_precision, num_components;
  265.   const char * process;
  266.   int ci;
  267.  
  268.   length = read_2_bytes();    /* usual parameter length count */
  269.  
  270.   data_precision = read_1_byte();
  271.   image_height = read_2_bytes();
  272.   image_width = read_2_bytes();
  273.   num_components = read_1_byte();
  274.  
  275.   switch (marker) {
  276.   case M_SOF0:    process = "Baseline";  break;
  277.   case M_SOF1:    process = "Extended sequential";  break;
  278.   case M_SOF2:    process = "Progressive";  break;
  279.   case M_SOF3:    process = "Lossless";  break;
  280.   case M_SOF5:    process = "Differential sequential";  break;
  281.   case M_SOF6:    process = "Differential progressive";  break;
  282.   case M_SOF7:    process = "Differential lossless";  break;
  283.   case M_SOF9:    process = "Extended sequential, arithmetic coding";  break;
  284.   case M_SOF10:    process = "Progressive, arithmetic coding";  break;
  285.   case M_SOF11:    process = "Lossless, arithmetic coding";  break;
  286.   case M_SOF13:    process = "Differential sequential, arithmetic coding";  break;
  287.   case M_SOF14:    process = "Differential progressive, arithmetic coding"; break;
  288.   case M_SOF15:    process = "Differential lossless, arithmetic coding";  break;
  289.   default:    process = "Unknown";  break;
  290.   }
  291.  
  292.   printf("JPEG image is %uw * %uh, %d color components, %d bits per sample\n",
  293.      image_width, image_height, num_components, data_precision);
  294.   printf("JPEG process: %s\n", process);
  295.  
  296.   if (length != (unsigned int) (8 + num_components * 3))
  297.     ERREXIT("Bogus SOF marker length");
  298.  
  299.   for (ci = 0; ci < num_components; ci++) {
  300.     (void) read_1_byte();    /* Component ID code */
  301.     (void) read_1_byte();    /* H, V sampling factors */
  302.     (void) read_1_byte();    /* Quantization table number */
  303.   }
  304. }
  305.  
  306.  
  307. /*
  308.  * Parse the marker stream until SOS or EOI is seen;
  309.  * display any COM markers.
  310.  * While the companion program wrjpgcom will always insert COM markers before
  311.  * SOFn, other implementations might not, so we scan to SOS before stopping.
  312.  * If we were only interested in the image dimensions, we would stop at SOFn.
  313.  * (Conversely, if we only cared about COM markers, there would be no need
  314.  * for special code to handle SOFn; we could treat it like other markers.)
  315.  */
  316.  
  317. static int
  318. scan_JPEG_header (int verbose)
  319. {
  320.   int marker;
  321.  
  322.   /* Expect SOI at start of file */
  323.   if (first_marker() != M_SOI)
  324.     ERREXIT("Expected SOI marker first");
  325.  
  326.   /* Scan miscellaneous markers until we reach SOS. */
  327.   for (;;) {
  328.     marker = next_marker();
  329.     switch (marker) {
  330.     case M_SOF0:        /* Baseline */
  331.     case M_SOF1:        /* Extended sequential, Huffman */
  332.     case M_SOF2:        /* Progressive, Huffman */
  333.     case M_SOF3:        /* Lossless, Huffman */
  334.     case M_SOF5:        /* Differential sequential, Huffman */
  335.     case M_SOF6:        /* Differential progressive, Huffman */
  336.     case M_SOF7:        /* Differential lossless, Huffman */
  337.     case M_SOF9:        /* Extended sequential, arithmetic */
  338.     case M_SOF10:        /* Progressive, arithmetic */
  339.     case M_SOF11:        /* Lossless, arithmetic */
  340.     case M_SOF13:        /* Differential sequential, arithmetic */
  341.     case M_SOF14:        /* Differential progressive, arithmetic */
  342.     case M_SOF15:        /* Differential lossless, arithmetic */
  343.       if (verbose)
  344.     process_SOFn(marker);
  345.       else
  346.     skip_variable();
  347.       break;
  348.  
  349.     case M_SOS:            /* stop before hitting compressed data */
  350.       return marker;
  351.  
  352.     case M_EOI:            /* in case it's a tables-only JPEG stream */
  353.       return marker;
  354.  
  355.     case M_COM:
  356.       process_COM();
  357.       break;
  358.  
  359.     default:            /* Anything else just gets skipped */
  360.       skip_variable();        /* we assume it has a parameter count... */
  361.       break;
  362.     }
  363.   } /* end loop */
  364. }
  365.  
  366.  
  367. /* Command line parsing code */
  368.  
  369. static const char * progname;    /* program name for error messages */
  370.  
  371.  
  372. static void
  373. usage (void)
  374. /* complain about bad command line */
  375. {
  376.   fprintf(stderr, "rdjpgcom displays any textual comments in a JPEG file.\n");
  377.  
  378.   fprintf(stderr, "Usage: %s [switches] [inputfile]\n", progname);
  379.  
  380.   fprintf(stderr, "Switches (names may be abbreviated):\n");
  381.   fprintf(stderr, "  -verbose    Also display dimensions of JPEG image\n");
  382.  
  383.   exit(EXIT_FAILURE);
  384. }
  385.  
  386.  
  387. static int
  388. keymatch (char * arg, const char * keyword, int minchars)
  389. /* Case-insensitive matching of (possibly abbreviated) keyword switches. */
  390. /* keyword is the constant keyword (must be lower case already), */
  391. /* minchars is length of minimum legal abbreviation. */
  392. {
  393.   register int ca, ck;
  394.   register int nmatched = 0;
  395.  
  396.   while ((ca = *arg++) != '\0') {
  397.     if ((ck = *keyword++) == '\0')
  398.       return 0;            /* arg longer than keyword, no good */
  399.     if (isupper(ca))        /* force arg to lcase (assume ck is already) */
  400.       ca = tolower(ca);
  401.     if (ca != ck)
  402.       return 0;            /* no good */
  403.     nmatched++;            /* count matched characters */
  404.   }
  405.   /* reached end of argument; fail if it's too short for unique abbrev */
  406.   if (nmatched < minchars)
  407.     return 0;
  408.   return 1;            /* A-OK */
  409. }
  410.  
  411.  
  412. /*
  413.  * The main program.
  414.  */
  415.  
  416. int
  417. main (int argc, char **argv)
  418. {
  419.   int argn;
  420.   char * arg;
  421.   int verbose = 0;
  422.  
  423.   /* On Mac, fetch a command line. */
  424. #ifdef USE_CCOMMAND
  425.   argc = ccommand(&argv);
  426. #endif
  427.  
  428.   progname = argv[0];
  429.   if (progname == NULL || progname[0] == 0)
  430.     progname = "rdjpgcom";    /* in case C library doesn't provide it */
  431.  
  432.   /* Parse switches, if any */
  433.   for (argn = 1; argn < argc; argn++) {
  434.     arg = argv[argn];
  435.     if (arg[0] != '-')
  436.       break;            /* not switch, must be file name */
  437.     arg++;            /* advance over '-' */
  438.     if (keymatch(arg, "verbose", 1)) {
  439.       verbose++;
  440.     } else
  441.       usage();
  442.   }
  443.  
  444.   /* Open the input file. */
  445.   /* Unix style: expect zero or one file name */
  446.   if (argn < argc-1) {
  447.     fprintf(stderr, "%s: only one input file\n", progname);
  448.     usage();
  449.   }
  450.   if (argn < argc) {
  451.     if ((infile = fopen(argv[argn], READ_BINARY)) == NULL) {
  452.       fprintf(stderr, "%s: can't open %s\n", progname, argv[argn]);
  453.       exit(EXIT_FAILURE);
  454.     }
  455.   } else {
  456.     /* default input file is stdin */
  457. #ifdef USE_SETMODE        /* need to hack file mode? */
  458.     setmode(fileno(stdin), O_BINARY);
  459. #endif
  460. #ifdef USE_FDOPEN        /* need to re-open in binary mode? */
  461.     if ((infile = fdopen(fileno(stdin), READ_BINARY)) == NULL) {
  462.       fprintf(stderr, "%s: can't open stdin\n", progname);
  463.       exit(EXIT_FAILURE);
  464.     }
  465. #else
  466.     infile = stdin;
  467. #endif
  468.   }
  469.  
  470.   /* Scan the JPEG headers. */
  471.   (void) scan_JPEG_header(verbose);
  472.  
  473.   /* All done. */
  474.   exit(EXIT_SUCCESS);
  475.   return 0;            /* suppress no-return-value warnings */
  476. }
  477.